﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using J_FoundationClasses.Programacao;

/*
 * Gerenciando os símbolos do programa fonte
 * Contém definicões de e referências para os símbolos
 * 
 */

namespace Montador
{
    public class ColSimbo
    {
        public DataTable SimDefs;
        public DataTable SimRefs;

        public ColSimbo()
        {
            // instanciar as tabelas SimDefs e SimRefs
            SimDefs = new DataTable();

            DataColumn colLexema = new DataColumn("Lexema", typeof(string));
            SimDefs.Columns.Add(colLexema);

            DataColumn colTipo = new DataColumn("Tipo", typeof(TipoSimbo));  
            SimDefs.Columns.Add(colTipo);
   
            SimDefs.PrimaryKey = new DataColumn[] { colLexema, colTipo };

            SimDefs.Columns.Add(new DataColumn("Definido", typeof(bool)));   // tem definição dentro deste programa fonte
            SimDefs.Columns.Add(new DataColumn("Externo", typeof(bool)));    // visível em outros objetos. possivelmente !Definido
            SimDefs.Columns.Add(new DataColumn("Vinculado", typeof(bool)));  // Valor átual em Endereço
            SimDefs.Columns.Add(new DataColumn("Start", typeof(bool)));  // Rótulo marca endereço de start de programa

            SimDefs.Columns.Add(new DataColumn("Linha", typeof(uint)));      // somente se Definido == True
            SimDefs.Columns.Add(new DataColumn("Endereco", typeof(UInt64))); // somente se Vinculado == True

            DataColumn colPosi = new DataColumn("Posicao", typeof(UInt32));  // posição na tabela de símbolos do objeto binário
            colPosi.DefaultValue = (object) 0;
            SimDefs.Columns.Add(colPosi);  

            // -----------

            SimRefs = new DataTable();

            colLexema = new DataColumn("Lexema", typeof(string));
            SimRefs.Columns.Add(colLexema);
            SimRefs.PrimaryKey = new DataColumn[] { colLexema };

            SimRefs.Columns.Add(new DataColumn("Linha", typeof(UInt32)));
        }

        public void InsSimDef(int prmFase, SimboloFonte prmSim, UInt32 prmLin)
        {
            // coloca símbolos, atributos e propriedades deles para a tabela.
            // descobre duplicados e inconsistências
            // devolve erros como Exception
            // procedimento em três fases organizado pelo caller, OrgaSimbo(..)

            String ff;
            DataRow rr;
            DataRow[] Resultado;

            switch (prmFase)
            {
                case 1:
                    // colocar rótulos com definição local. prmSim já é testado "limpo".
                    rr = SimDefs.NewRow();
                    rr["Lexema"] = prmSim.Lexema;
                    rr["Tipo"] = prmSim.Tipo;  // pode ser rotulo ou programa
                    rr["Definido"] = true;
                    rr["Externo"] = false;
                    rr["Vinculado"] = (prmSim.Tipo == TipoSimbo.Rotulo);
                    rr["Start"] = false;
                    rr["Linha"] = prmLin;
                    rr["Endereco"] = prmSim.edcLocal;
                    SimDefs.Rows.Add(rr);  // se já existe, caller deve capturar ConstraintException
                    break;
                case 2:
                    // modificar propriedades de rótulos existentes

                    if (prmSim.Tipo == TipoSimbo.Programa) break; // não tem modificação para programas

                    // acesse o rótulo a modifiar
                    ff = String.Format("Lexema = '{0}' AND Tipo = {1}", prmSim.Lexema, (int) prmSim.Tipo);
                    Resultado = SimDefs.Select(ff);  // 0 ou 1 resultado. Não mais pq tem PrimaryKey constraint

                    // número de ocorrências...???
                    if (Resultado.Length > 1) throw new ApplicationException(String.Format("Rótulo {1} existe {1} vezes.", prmSim.Lexema, Resultado.Length));
                    if (Resultado.Length == 0)
                    {
                        if (prmSim.Externo) break;  // declaração externa é legítima mesmo sem rótulo definido. Faça o necessário na 3. fase
                        throw new ApplicationException(String.Format("Rótulo {0} não existe.", prmSim.Lexema));
                    }

                    // agora a modificação:
                    Resultado[0]["Externo"] = prmSim.Externo;
                    Resultado[0]["Start"] = prmSim.Start;
                    break;
                case 3:
                    // inserir rótulos externos não definidos localmente (= import label)
                    if (prmSim.Tipo != TipoSimbo.Rotulo) break;
                    rr = SimDefs.NewRow();
                    rr["Lexema"] = prmSim.Lexema;
                    rr["Tipo"] = prmSim.Tipo;
                    rr["Definido"] = prmSim.Definido;
                    rr["Externo"] = prmSim.Externo;
                    rr["Vinculado"] = false;  // os vinculados já eram inseridos no case 1:
                    rr["Start"] = prmSim.Start;
                    rr["Linha"] = prmLin;
                    try { SimDefs.Rows.Add(rr); 
                    } catch (ConstraintException) {
                     /* se jé existe: ignorar */ } 
                    break;
                default:
                    throw new ApplicationException(String.Format("Não tem fase {0}.", prmFase));
            } // switch
        }
        public SimboloFonte InsSimRef(FixupFonte prmFix, int prmLinha)
        {
            // coloca referências aso símbolos.
            // descobre referências aos símbolos inexistentes ou de típo errado
            // devolve erros como Exception

            if (prmFix == null) throw new ApplicationException("Parâmetro do InsSimRef não deve ser null.");

            // buscar símbolo
            object[] Key = new object[] { prmFix.Rotulo, TipoSimbo.Rotulo };
            DataRow Resultado = SimDefs.Rows.Find(Key);

            // Não tem..? Que pena!
            if (Resultado == null) throw new ApplicationException(String.Format("Rótulo {0} não encontrado na tabela de símbolos.", prmFix.Rotulo));

            // Marcamos a referência
            DataRow rr = SimRefs.NewRow();
            rr["Lexema"] =  prmFix.Rotulo;
            rr["Linha"] =  prmLinha;
            SimRefs.Rows.Add(rr);

            // publicamos o símbolo encontrado
            SimboloFonte si = new SimboloFonte(prmFix.Rotulo);
            si.Tipo = (TipoSimbo) Resultado["Tipo"];
            si.Start = (bool)Resultado["Start"];
            si.Definido = (bool)Resultado["Definido"];
            si.Externo = (bool) Resultado["Externo"];
            if (si.Definido) si.edcLocal =(UInt64)  Resultado["Endereco"];
            return si;
        }

    }
}
